package com.ruicar.afs.cloud.batch.frame.control;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.ruicar.afs.cloud.batch.entity.AfsBatchInfo;
import com.ruicar.afs.cloud.batch.entity.AfsBatchLogStep;
import com.ruicar.afs.cloud.batch.enums.ErrorType;
import com.ruicar.afs.cloud.batch.enums.MessageType;
import com.ruicar.afs.cloud.batch.frame.bean.BatchRunGroupInfo;
import com.ruicar.afs.cloud.batch.frame.bean.BatchRunStepInfo;
import com.ruicar.afs.cloud.batch.frame.logger.WsMonitorHelper;
import com.ruicar.afs.cloud.batch.service.AfsBatchInfoService;
import com.ruicar.afs.cloud.batch.service.AfsBatchLogGroupService;
import com.ruicar.afs.cloud.batch.service.AfsBatchLogStepService;
import com.ruicar.afs.cloud.common.core.enums.AfsEnumUtil;
import com.ruicar.afs.cloud.common.core.util.Base64Util;
import com.ruicar.afs.cloud.common.core.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

@Slf4j
public class BatchGroupControlRunner extends BatchControlRunner {
    private final CountDownLatch superCountDownLatch ;
    private final BatchRunGroupInfo batchRunGroupInfo;
    private final AfsBatchLogGroupService afsBatchLogGroupService;
    private final AfsBatchLogStepService afsBatchLogStepService;
    private final AfsBatchInfoService afsBatchInfoService;
    private final MonitorControl monitorControl;
    private ErrorType errorType = ErrorType.SELF;
    public BatchGroupControlRunner(CountDownLatch superCountDownLatch, BatchRunGroupInfo batchRunGroupInfo, AfsBatchLogGroupService afsBatchLogGroupService, AfsBatchLogStepService afsBatchLogStepService, AfsBatchInfoService afsBatchInfoService, MonitorControl monitorControl) {
        this.superCountDownLatch = superCountDownLatch;
        this.batchRunGroupInfo = batchRunGroupInfo;
        this.afsBatchLogGroupService = afsBatchLogGroupService;
        this.afsBatchLogStepService = afsBatchLogStepService;
        this.afsBatchInfoService = afsBatchInfoService;
        this.monitorControl = monitorControl;
    }

    private void changeBatchToError(AfsBatchInfo afsBatchInfo,String expStack){
        afsBatchInfoService.changeBatchToError(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName());
        JSONObject errorNotice = JSONObject.parseObject(afsBatchInfo.getErrorNoticeConfig(),JSONObject.class);
        if(errorNotice.containsKey("security")&&errorNotice.containsKey("hockUri")){
            ThreadUtil.execute(()->{
                if(StringUtils.isNotEmpty(errorNotice.getString("security"))){
                    try {
                        Long timestamp = System.currentTimeMillis();
                        String secret = errorNotice.getString("security");
                        String stringToSign = timestamp + "\n" + secret;
                        Mac mac = Mac.getInstance("HmacSHA256");
                        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
                        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
                        String sign = URLEncoder.encode(Base64Util.encode(signData), "UTF-8");
                        DingTalkClient client = new DefaultDingTalkClient(errorNotice.getString("hockUri")+"&timestamp="+timestamp+"&sign="+sign);
                        OapiRobotSendRequest request = new OapiRobotSendRequest();
                        request.setMsgtype("markdown");
                        OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
                        markdown.setTitle("批量出错");
                        AfsBatchLogStep afsBatchLogStep = SpringContextHolder.getBean(AfsBatchLogStepService.class).queryError(afsBatchInfo.getBatchDate());
                        StringBuffer stringBuffer = new StringBuffer();

                        if(afsBatchLogStep!=null){
                            String simpleStack = StringUtils.isEmpty(afsBatchLogStep.getStepExceptionStack())?"未知错误":afsBatchLogStep.getStepExceptionStack().substring(0,40);
                            stringBuffer.append("<font color=#FF0000 size=4 face=\"黑体\">批量出错 </font>\n")
                                    .append("##### **批量日期**:")
                                    .append(afsBatchInfo.getBatchDate())
                                    .append("\n")
                                    .append("##### **系统编号**:")
                                    .append(afsBatchInfo.getSystemName())
                                    .append("\n")
                                    .append("##### **出错任务编号**:")
                                    .append(MessageFormat.format("{0}-{1}-{2}",afsBatchLogStep.getSystemName(),afsBatchLogStep.getBatchNo(),afsBatchLogStep.getStepNo()))
                                    .append("\n")
                                    .append("##### **异常简要信息**:")
                                    .append(simpleStack);
                        }else {
                            stringBuffer.append("<font color=#FF0000 size=4 face=\"黑体\">批量出错 </font>\n")
                                    .append("##### **批量日期**:")
                                    .append(afsBatchInfo.getBatchDate())
                                    .append("\n")
                                    .append("##### **系统编号**:")
                                    .append(afsBatchInfo.getSystemName())
                                    .append("##### **异常简要信息**:")
                                    .append(StringUtils.isEmpty(expStack)?"请查询系统日志":expStack);
                            ;
                        }
                        markdown.setText(stringBuffer.toString());
                        request.setMarkdown(markdown);
                        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
                        at.setAtMobiles(new ArrayList<>());
                        at.setIsAtAll(true);
                        request.setAt(at);
                        OapiRobotSendResponse response = client.execute(request);
                        if(!response.isSuccess()){
                            log.error("=========批量异常信息通知出错，钉钉返回{}=========",response.getErrmsg());
                        }
                    }catch (Exception exception){
                        log.error("",exception);
                    }
                }
            });
        }
    }

    private String getNextWorkDay(AfsBatchInfo afsBatchInfo){
        try {
            return BatchHelper.nextWorkDay(afsBatchInfo.getBatchDate());
        }catch (Exception e){
            if(batchRunGroupInfo.isRootGroup()){
                changeBatchToError(afsBatchInfo,e.getMessage());
            }
            WsMonitorHelper.sendMessage(MessageType.ERROR,MessageFormat.format("批量获取下一个工作日异常【{0}】",e.getMessage()));
            throw e;
        }
    }

    @Override
    public void run() {
        monitorControl.add(this);
        AfsBatchInfo afsBatchInfo =afsBatchInfoService.getSystemBatchInfoWithOutUpdate(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName());
        if(batchRunGroupInfo.isResume()){
            try {
                log.info("===========日初批量异常恢复日初批量执行,批量日期[{}]===========", afsBatchInfo.getBatchDate());
                WsMonitorHelper.sendMessage(MessageType.INFO, MessageFormat.format("===========日初批量异常恢复日初批量执行,批量日期[{0}]===========", afsBatchInfo.getBatchDate()));
                runGroup();
            }finally {
                if(batchRunGroupInfo.isRootGroup()){
                    monitorControl.setFinished();
                }
                this.setFinished();
            }
        }else {
            if (batchRunGroupInfo.isRootGroup()) {
                log.info("===========批量节假日模式[{}-{}]========", AfsEnumUtil.key(afsBatchInfo.getHolidayMode()), AfsEnumUtil.desc(afsBatchInfo.getHolidayMode()));
                WsMonitorHelper.sendMessage(MessageType.INFO, MessageFormat.format("===========批量节假日模式[{0}-{1}]========", AfsEnumUtil.key(afsBatchInfo.getHolidayMode()), AfsEnumUtil.desc(afsBatchInfo.getHolidayMode())));
                try {
                    switch (afsBatchInfo.getHolidayMode()) {
                        case HUMAN_DAY_BY_DAY:
                        case DIRECT_RUN_TO_TARGET:
                            runGroup();
                            break;
                        case REPEAT_DAY_BY_DAY_AUTO: {
                            String nextWorkDay = getNextWorkDay(afsBatchInfo);
                            long between = DateUtil.betweenDay(DateUtil.parse(afsBatchInfo.getBatchDate(), DatePattern.PURE_DATE_PATTERN), DateUtil.parse(nextWorkDay, DatePattern.PURE_DATE_PATTERN), true);
                            log.info("===========批量节假日模式[{}-{}]===一共需要执行[{}]天========", AfsEnumUtil.key(afsBatchInfo.getHolidayMode()), AfsEnumUtil.desc(afsBatchInfo.getHolidayMode()), between);
                            WsMonitorHelper.sendMessage(MessageType.INFO, MessageFormat.format("===========批量节假日模式[{0}-{1}]========一共需要执行[{2}]天========", AfsEnumUtil.key(afsBatchInfo.getHolidayMode()), AfsEnumUtil.desc(afsBatchInfo.getHolidayMode()), between));
                            for (int i = 0; i < between; i++) {
                                runGroup();
                            }
                            break;
                        }
                    }
                } finally {
                    if (batchRunGroupInfo.isRootGroup()) {
                        monitorControl.setFinished();
                    }
                    this.setFinished();
                }
            } else {
                try {
                    runGroup();
                } finally {
                    if (batchRunGroupInfo.isRootGroup()) {
                        monitorControl.setFinished();
                    }
                    this.setFinished();
                }
            }
        }
    }



    private void runGroup(){
        AfsBatchInfo afsBatchInfo =afsBatchInfoService.getSystemBatchInfoWithOutUpdate(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName());
        WsMonitorHelper.sendMessage(MessageType.INFO,MessageFormat.format("===========当前批量日[{0}],上一工作日[{1}]========",afsBatchInfo.getBatchDate(),afsBatchInfo.getLastWorkDay()));
        try {
            //执行自己的步骤
            log.info("========={}-{}需要执行的步骤数{}=========",batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(),batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchNo(),batchRunGroupInfo.getBatchRunStepInfos().size());
            boolean exceptionFlag = false;
            CountDownLatch countDownLatch = null;
            Throwable throwable = null;
            if(batchRunGroupInfo.getBatchRunStepInfos().size()>0) {
                countDownLatch = new CountDownLatch(batchRunGroupInfo.getBatchRunStepInfos().size());
                afsBatchLogGroupService.doLog(batchRunGroupInfo.getAfsBatchJobGroupInfo(), afsBatchInfo.getBatchDate());
                AfsGroupRunnerExceptionHandler afsGroupRunnerExceptionHandler = new AfsGroupRunnerExceptionHandler();
                List<BatchStepControlRunner> batchStepControlRunners = new ArrayList<>();
                for (BatchRunStepInfo batchRunStepInfo : batchRunGroupInfo.getBatchRunStepInfos()) {
                    afsBatchInfoService.addRunThreadNum(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(), 1, MessageFormat.format("StepRunner-{0}-{1}-{2}", afsBatchInfo.getSystemName(), batchRunStepInfo.getAfsBatchJobStepInfo().getBatchNo(), batchRunStepInfo.getAfsBatchJobStepInfo().getStepNo()));
                    BatchStepControlRunner batchStepControlRunner = new BatchStepControlRunner(batchRunStepInfo, countDownLatch, afsBatchLogStepService, afsBatchInfoService, monitorControl);
                    batchStepControlRunner.setUncaughtExceptionHandler(afsGroupRunnerExceptionHandler);
                    batchStepControlRunner.setDaemon(false);
                    batchStepControlRunner.setName(MessageFormat.format("批量步骤-{0}-{1}-{2}", batchRunStepInfo.getAfsBatchJobStepInfo().getSystemName(), batchRunStepInfo.getAfsBatchJobStepInfo().getBatchNo(), batchRunStepInfo.getAfsBatchJobStepInfo().getStepName()));
                    batchStepControlRunners.add(batchStepControlRunner);
                }
                afsGroupRunnerExceptionHandler.setBatchGroupControlRunners(batchStepControlRunners);
                for (BatchStepControlRunner batchStepControlRunner : batchStepControlRunners) {
                    batchStepControlRunner.start();
                }
                countDownLatch.await();
                for (BatchStepControlRunner batchStepControlRunner : batchStepControlRunners) {
                    if (batchStepControlRunner.getThrowable() != null) {
                        if (throwable == null) {
                            throwable = batchStepControlRunner.getThrowable();
                        }
                        exceptionFlag = true;
                    }
                }
            }
            setSelfFinished(true);
            if(!exceptionFlag){
                if(batchRunGroupInfo.getBatchRunStepInfos().size()>0) {
                    afsBatchLogGroupService.finished(batchRunGroupInfo.getAfsBatchJobGroupInfo(), afsBatchInfo.getBatchDate());
                }
                //执行子组的步骤
                countDownLatch = new CountDownLatch(batchRunGroupInfo.getSubGroups().size());
                List<BatchGroupControlRunner> batchGroupControlRunners = new ArrayList<>();
                for (BatchRunGroupInfo subGroup : batchRunGroupInfo.getSubGroups()) {
                    afsBatchInfoService.addRunThreadNum(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(),1,MessageFormat.format("GroupRunner-{0}-{1}",afsBatchInfo.getSystemName(),subGroup.getAfsBatchJobGroupInfo().getBatchNo()));
                    BatchGroupControlRunner batchGroupControlRunner = new BatchGroupControlRunner(countDownLatch,subGroup, afsBatchLogGroupService, afsBatchLogStepService,afsBatchInfoService,monitorControl);
                    batchGroupControlRunner.setDaemon(true);
                    batchGroupControlRunner.setName(MessageFormat.format("批量组-{0}-{1}-{2}",subGroup.getAfsBatchJobGroupInfo().getSystemName(),subGroup.getAfsBatchJobGroupInfo().getBatchNo(),subGroup.getAfsBatchJobGroupInfo().getBatchName()));
                    batchGroupControlRunners.add(batchGroupControlRunner);
                    batchGroupControlRunner.start();
                }
                countDownLatch.await();
                for ( BatchGroupControlRunner batchGroupControlRunner : batchGroupControlRunners) {
                    if(batchGroupControlRunner.getThrowable()!=null){
                        throwable = batchGroupControlRunner.getThrowable();
                        errorType = ErrorType.SUB;
                        throw throwable;
                    }
                }
                setSelfFinished(true);
            }else{
                afsBatchLogGroupService.saveError(batchRunGroupInfo.getAfsBatchJobGroupInfo(),afsBatchInfo.getBatchDate());
                throw throwable;
            }
        }catch (Throwable e){
            if(errorType==ErrorType.SELF) {
                afsBatchLogGroupService.saveError(batchRunGroupInfo.getAfsBatchJobGroupInfo(), afsBatchInfo.getBatchDate());
                log.error(MessageFormat.format("=========任务[{0}]-[{1}]-[{2}]运行异常=========",batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(),batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchNo(),batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchName()),e);
                WsMonitorHelper.sendMessage(MessageType.ERROR,MessageFormat.format("任务[{0}]-[{1}]-[{2}]运行异常",batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(),batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchNo(),batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchName()));
                WsMonitorHelper.sendMessage(MessageType.ERROR,ExceptionUtil.stacktraceToString(e,5000));
            }
            if(batchRunGroupInfo.isRootGroup()){
                changeBatchToError(afsBatchInfo,null);
            }
            this.setThrowable(e);
            throw new RuntimeException(e);
        }finally {
            if (superCountDownLatch!=null){
                superCountDownLatch.countDown();
                afsBatchInfoService.cutDown(batchRunGroupInfo.getAfsBatchJobGroupInfo().getSystemName(),1,MessageFormat.format("GroupRunner-{0}-{1}",afsBatchInfo.getSystemName(),this.batchRunGroupInfo.getAfsBatchJobGroupInfo().getBatchNo()));
            }
        }
    }
}
